package org.wxd.bi.data;

import com.alibaba.fastjson.JSONObject;
import io.netty.handler.codec.http.HttpHeaderNames;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.wxd.aop.Aop;
import org.wxd.batis.sql.mysql.MysqlDataHelper;
import org.wxd.batis.sql.mysql.MysqlFactory;
import org.wxd.bi.po.AppInfo;
import org.wxd.bi.po.GlobInfo;
import org.wxd.bi.po.UserInfo;
import org.wxd.i.IStart;
import org.wxd.net.web.hs.HttpSession;
import org.wxd.str.Md5Util;
import org.wxd.str.StringUtil;
import org.wxd.system.ClassUtil;

import javax.annotation.Resource;
import java.io.Serializable;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * @author: Troy.Chen(無心道, 15388152619)
 * @version: 2021-10-21 16:27
 **/
@Resource
public class AppFactory implements Serializable, IStart {

    private static final long serialVersionUID = 1L;
    private static final Logger log = LoggerFactory.getLogger(AppFactory.class);

    @Resource
    private MysqlFactory dataFactory;

    private Map<Integer, AppService> dataServiceMap = new LinkedHashMap<>();

    @Override
    public AppFactory start() throws Exception {
        MysqlDataHelper dataHelper = dataFactory.dataHelper();
        Collection<Class<?>> classInfos = ClassUtil.getClasses(UserInfo.class.getPackage().getName());
        for (Class<?> loadClass : classInfos) {
            dataHelper.createTable(loadClass);
        }

        checkGlobInfo("appid", 10000);

        checkGlobInfo("userid", 10000);

        {
            UserInfo wxd = queryUserInfo("root");
            if (wxd == null) {
                String randomString = StringUtil.getRandomString(8);
                wxd = new UserInfo()
                        .setUserId(newUserId())
                        .setUserName("root")
                        .setUserPwd(Md5Util.md5DigestEncode(randomString))
                        .setAdmin(true)
                        .setCreateTime(System.currentTimeMillis())
                        .setLastLoginTime(System.currentTimeMillis());
                dataHelper.replace(wxd);
                log.warn("create admin " + wxd.getUserName() + "--" + randomString);
            }
        }
        List<AppInfo> appInfos = dataHelper.queryEntities(AppInfo.class);
        for (AppInfo appInfo : appInfos) {
            dataService(appInfo.getAppId());
        }
        return this;
    }

    public int newUserId() throws Exception {
        return newGlobInt("userid");
    }

    public int newAppId() throws Exception {
        return newGlobInt("appid");
    }

    public synchronized int newGlobInt(String key) throws Exception {
        MysqlDataHelper dataHelper = dataFactory.dataHelper();
        GlobInfo globInfo = dataHelper.queryEntity(GlobInfo.class, key);
        int value = globInfo.valueOf(int.class);
        value++;
        globInfo.setValue(String.valueOf(value));
        dataHelper.replace(globInfo);
        return value;
    }

    public synchronized long newGlobLong(String key) throws Exception {
        MysqlDataHelper dataHelper = dataFactory.dataHelper();
        GlobInfo globInfo = dataHelper.queryEntity(GlobInfo.class, key);
        long value = globInfo.valueOf(long.class);
        value++;
        globInfo.setValue(String.valueOf(value));
        dataHelper.replace(globInfo);
        return value;
    }

    /**
     * @param key       key
     * @param initValue 初始值
     * @throws Exception
     */
    public synchronized void checkGlobInfo(String key, Object initValue) throws Exception {
        MysqlDataHelper dataHelper = dataFactory.dataHelper();
        GlobInfo userid = dataHelper.queryEntity(GlobInfo.class, key);
        if (userid == null) {
            userid = new GlobInfo();
            userid.setKey(key);
            userid.setValue(String.valueOf(initValue));
            dataHelper.replace(userid);
        }
    }

    public synchronized AppService dataService(JSONObject putData) throws Exception {
        int appId = putData.getIntValue("appId");
        AppService appService = Aop.getBean(AppFactory.class).dataService(appId);
        if (appService == null) {
            return null;
        }
        return appService;
    }

    public synchronized AppService dataService(int appId) throws Exception {
        AppService service = dataServiceMap.get(appId);
        if (service == null) {
            MysqlDataHelper dataHelper = dataFactory.dataHelper();
            AppInfo appInfo = dataHelper.queryEntity(AppInfo.class, appId);
            if (appInfo == null) return null;
            MysqlDataHelper appDataHelper = dataFactory.dataHelper(dataFactory.getDbConfig().getDbName() + "_" + appId);
            appDataHelper.initBatchPool(1);
            service = new AppService(appInfo, appDataHelper);
            dataServiceMap.put(appId, service);
        }
        return service;
    }

    public UserInfo queryUserInfo(HttpSession session) throws Exception {
        final String cookieValue = session.reqCookieValue(HttpHeaderNames.AUTHORIZATION);
        if (cookieValue != null) {
            final String[] split = cookieValue.split("\\.");
            if (split.length > 1) {
                final String userName = split[1];
                return queryUserInfo(userName);
            }
        }
        return null;
    }

    public UserInfo queryUserInfo(String userName) throws Exception {
        MysqlDataHelper mysqlDataHelper = dataFactory.dataHelper();
        String whereSql = mysqlDataHelper.getDataWrapper().columnName(UserInfo::getUserName);
        whereSql += " = ?";
        return mysqlDataHelper.queryEntityByWhere(UserInfo.class, whereSql, userName);
    }

    public UserInfo queryUserInfo(int userId) throws Exception {
        return dataFactory.dataHelper().queryEntity(UserInfo.class, userId);
    }

    public AppInfo queryAppInfo(int appId) throws Exception {
        return dataFactory.dataHelper().queryEntity(AppInfo.class, appId);
    }

    public MysqlFactory getDataFactory() {
        return dataFactory;
    }

    public Map<Integer, AppService> getDataServiceMap() {
        return dataServiceMap;
    }
}
